module dungeon.formula;

import std.string;

import nade.ast;
import nade.stack;

import dungeon.stats;


class Formula {
	this(AST ast)
	{
		foreach(node; ast)
		{
			switch(node.label)
			{
				case "+": _commands ~= new MathCommand!("+")(); break;
				case "-": _commands ~= new MathCommand!("-")(); break;
				case "*": _commands ~= new MathCommand!("*")(); break;
				case "/": _commands ~= new MathCommand!("/")(); break;

				default:
				{
					if(isNumeric(node.as!string))
						_commands ~= new NumberCommand(node.as!real);
					else
						_commands ~= new StatCommand(node.as!string);
				}
			}
		}
	}

	real evaluate(Stats stats)
	{
		Stack!(real) stack;

		foreach(command; _commands)
			command.evaluate(stack, stats);

		assert(stack.length == 1);
		return stack.peek;
	}

	protected {
		Command[]		_commands;
	}
}


class Command {
	abstract void evaluate(ref Stack!(real) stack, Stats stats);
}


class NumberCommand : Command {
	this(real value)
	{
		_value = value;
	}

	override void evaluate(ref Stack!(real) stack, Stats stats)
	{
		stack.push(_value);
	}

	protected {
		real		_value;
	}
}


class StatCommand : Command {
	this(string id)
	{
		_id = id;
	}

	override void evaluate(ref Stack!(real) stack, Stats stats)
	{
		stack.push(stats[_id]);
	}

	protected {
		string		_id;
	}
}


class MathCommand(string OP) : Command {
	this()
	{
	}

	override void evaluate(ref Stack!(real) stack, Stats stats)
	{
		auto b = stack.pop;
		mixin("stack.push(stack.pop " ~ OP ~ " b);");
	}
}
